package org.skatiger.jrove;

import java.util.Iterator;
import java.util.Map;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import org.skatiger.jrove.component.UIComponent;
import org.skatiger.jrove.component.UIViewRoot;
import org.skatiger.jrove.renderer.Renderer;

public abstract class ViewContext {
    public static final String VIEW_CONTEXT_IN_ELCONTEXT_KEY = "~jrove.ViewContext~";

    /**
     * get attribute from viewScope
     * 
     * @param key
     * @throws NullPointerException
     *             key is null
     * @return
     */
    public abstract Object getAttribute(String key);

    /**
     * get attribute from viewScope
     * 
     * @param key
     * @throws NullPointerException
     *             key is null
     * @return
     */
    public abstract Map<String, Object> getAttributes();

    /**
     * get viewScope attributes name
     * 
     * @return attribute names in viewscope
     */
    public abstract Iterator<String> getAttributeNames();

    /**
     * remove attribute from view scope
     * 
     * @param name
     *            attribute name
     */
    public abstract void removeAttribute(String name);

    /**
     * <p>
     * put attribute to view scope.
     * </p>
     * if value is null, will remove attribute from view scope
     * 
     * @param name
     *            attribute name
     * @param value
     *            attribute value
     */
    public abstract void setAttribute(String name, Object value);

    /**
     * @see ResponseWriter
     * 
     * @return current view response writer
     */
    public abstract ResponseWriter getResponseWriter();

    /**
     * @see ExternalContext
     * @return current servlet context objercts
     */
    public abstract ExternalContext getExternalContext();

    /**
     * init set {@link ResponseWriter} do not use this method.
     * 
     * @param writer
     */
    public abstract void setResponseWriter(ResponseWriter writer);

    /**
     * set view root component
     * 
     * @param root
     */
    public abstract void setViewRoot(UIViewRoot root);

    /**
     * jrove configuration
     * 
     * @return {@link Configurator}
     */
    public abstract Configurator getConfigurator();

    /**
     * expression language context in this view context
     * 
     * @return {@link ELContext}
     */
    public abstract ELContext getELContext();

    /**
     * expression factory
     * 
     * @return {@link ExpressionFactory}
     */
    public abstract ExpressionFactory getExpressionFactory();

    /**
     * @see UIViewRoot
     * @return current view root component
     */
    public abstract UIViewRoot getViewRoot();

    /**
     * view complete.
     */
    public abstract void complete();

    /**
     * is completed
     * 
     * @return if response complete, will return <code>true</code>
     */
    public abstract boolean isCompleted();

    /**
     * view context release();
     */
    public abstract void release();

    /**
     * create component by class componentClass
     * 
     * @param componentClass
     *            component class name
     * @return
     */
    public abstract UIComponent createComponent(String componentClass);

    /**
     * get renderer by renderer type
     * 
     * @param rendererType
     * @return
     */
    public abstract Renderer getRenderer(String rendererType);

    public abstract void appendELResolver(ELResolver elresolver);

    /**
     * create {@link ValueExpression} by expression
     * 
     * @param expression
     *            expression
     * @throws ELException
     *             when expression can not compile
     * @return {@link ValueExpression}
     */
    public abstract ValueExpression createValueExpression(String expression) throws ELException;

    /**
     * 
     * @param expression
     * @return
     * @throws ELException
     */
    public abstract Object getExpressionValue(String expression) throws ELException;

    /**
     * find current viewcontext in elcontext
     * 
     * @param elctx
     * @return
     */
    public static final ViewContext findViewContext(ELContext elctx) {
        Object context = elctx.getContext(ViewContext.class);
        if (context != null
                && ViewContext.class.isAssignableFrom(context.getClass())) {
            return (ViewContext) context;
        }
        return ViewContext.getCurrentInstance();
    }

    private static ThreadLocal<ViewContext> _currentInstance = new ThreadLocal<ViewContext>() {
        protected ViewContext initialValue() {
            return null;
        }
    };

    private static ThreadLocal<ViewContext> _firstInstance = new ThreadLocal<ViewContext>() {
        protected ViewContext initialValue() {
            return null;
        }
    };

    public static ViewContext getCurrentInstance() {
        return _currentInstance.get();
    }

    protected static void setCurrentInstance(ViewContext context) {
        if (context == null) {
            _currentInstance.remove();
            _firstInstance.remove();
        } else {
            _currentInstance.set(context);

            if (_firstInstance.get() == null) {
                _firstInstance.set(context);
            }
        }
    }

}
